﻿Imports System
Imports System.CodeDom
Imports System.Collections.Generic
Imports System.Diagnostics.CodeAnalysis
Imports System.Globalization
Imports System.Reflection
Imports System.Text
Imports System.Workflow.ComponentModel.Compiler

Namespace System.Workflow.Activities.Rules
    Friend Class MethodInvokeExpression
        Inherits RuleExpressionInternal
        ' Methods
        Friend Overrides Sub AnalyzeUsage(ByVal expression As CodeExpression, ByVal analysis As RuleAnalysis, ByVal isRead As Boolean, ByVal isWritten As Boolean, ByVal qualifier As RulePathQualifier)
            Dim expression2 As CodeMethodInvokeExpression = DirectCast(expression, CodeMethodInvokeExpression)
            Dim targetObject As CodeExpression = expression2.Method.TargetObject
            If (analysis.Validation.ExpressionInfo(targetObject) Is Nothing) Then
                Dim exception As New InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated, New Object(0  - 1) {}))
                exception.Data.Item("ErrorObject") = targetObject
                Throw exception
            End If
            Dim info2 As RuleMethodInvokeExpressionInfo = [TryCast](analysis.Validation.ExpressionInfo(expression2),RuleMethodInvokeExpressionInfo)
            If (info2 Is Nothing) Then
                Dim exception2 As New InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated, New Object(0  - 1) {}))
                exception2.Data.Item("ErrorObject") = expression2
                Throw exception2
            End If
            Dim methodInfo As MethodInfo = info2.MethodInfo
            Dim attributedExprs As New List([Of] CodeExpression)
            analysis.AnalyzeRuleAttributes(methodInfo, targetObject, qualifier, expression2.Parameters, methodInfo.GetParameters, attributedExprs)
            If Not attributedExprs.Contains(targetObject) Then
                RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, True, False, Nothing)
            End If
            Dim i As Integer
            For i = 0 To expression2.Parameters.Count - 1
                Dim item As CodeExpression = expression2.Parameters.Item(i)
                If Not attributedExprs.Contains(item) Then
                    RuleExpressionWalker.AnalyzeUsage(analysis, item, True, False, Nothing)
                End If
            Next i
        End Sub

        Friend Overrides Function Clone(ByVal expression As CodeExpression) As CodeExpression
            Dim expression2 As CodeMethodInvokeExpression = DirectCast(expression, CodeMethodInvokeExpression)
            Dim expression3 As New CodeMethodInvokeExpression { _
                .Method = MethodInvokeExpression.CloneMethodReference(expression2.Method) _
            }
            Dim expression4 As CodeExpression
            For Each expression4 In expression2.Parameters
                expression3.Parameters.Add(RuleExpressionWalker.Clone(expression4))
            Next
            Return expression3
        End Function

        Private Shared Function CloneMethodReference(ByVal oldReference As CodeMethodReferenceExpression) As CodeMethodReferenceExpression
            Dim result As New CodeMethodReferenceExpression { _
                .MethodName = oldReference.MethodName, _
                .TargetObject = RuleExpressionWalker.Clone(oldReference.TargetObject) _
            }
            Dim reference As CodeTypeReference
            For Each reference In oldReference.TypeArguments
                result.TypeArguments.Add(TypeReferenceExpression.CloneType(reference))
            Next
            ConditionHelper.CloneUserData(oldReference, result)
            Return result
        End Function

        Friend Overrides Sub Decompile(ByVal expression As CodeExpression, ByVal stringBuilder As StringBuilder, ByVal parentExpression As CodeExpression)
            Dim expression2 As CodeMethodInvokeExpression = DirectCast(expression, CodeMethodInvokeExpression)
            If ((expression2.Method Is Nothing) OrElse (expression2.Method.TargetObject Is Nothing)) Then
                Dim exception As New RuleEvaluationException(String.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, New Object() { expression2.Method.MethodName }))
                exception.Data.Item("ErrorObject") = expression2
                Throw exception
            End If
            Dim targetObject As CodeExpression = expression2.Method.TargetObject
            RuleExpressionWalker.Decompile(stringBuilder, targetObject, expression2)
            stringBuilder.Append("."c)
            stringBuilder.Append(expression2.Method.MethodName)
            stringBuilder.Append("("c)
            If (Not expression2.Parameters Is Nothing) Then
                Dim i As Integer
                For i = 0 To expression2.Parameters.Count - 1
                    Dim expression4 As CodeExpression = expression2.Parameters.Item(i)
                    If (expression4 Is Nothing) Then
                        Dim exception2 As New RuleEvaluationException(String.Format(CultureInfo.CurrentCulture, Messages.NullMethodTypeParameter, New Object() { i.ToString(CultureInfo.CurrentCulture), expression2.Method.MethodName }))
                        exception2.Data.Item("ErrorObject") = expression2
                        Throw exception2
                    End If
                    If (i > 0) Then
                        stringBuilder.Append(", ")
                    End If
                    RuleExpressionWalker.Decompile(stringBuilder, expression4, Nothing)
                Next i
            End If
            stringBuilder.Append(")"c)
        End Sub

        Friend Overrides Function Evaluate(ByVal expression As CodeExpression, ByVal execution As RuleExecution) As RuleExpressionResult
            Dim obj3 As Object
            Dim expression2 As CodeMethodInvokeExpression = DirectCast(expression, CodeMethodInvokeExpression)
            Dim obj2 As Object = RuleExpressionWalker.Evaluate(execution, expression2.Method.TargetObject).Value
            Dim info As RuleMethodInvokeExpressionInfo = [TryCast](execution.Validation.ExpressionInfo(expression2),RuleMethodInvokeExpressionInfo)
            If (info Is Nothing) Then
                Dim exception As New InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated, New Object(0  - 1) {}))
                exception.Data.Item("ErrorObject") = expression2
                Throw exception
            End If
            Dim methodInfo As MethodInfo = info.MethodInfo
            If (Not methodInfo.IsStatic AndAlso (obj2 Is Nothing)) Then
                Dim exception2 As New RuleEvaluationException(String.Format(CultureInfo.CurrentCulture, Messages.TargetEvaluatedNullMethod, New Object() { expression2.Method.MethodName }))
                exception2.Data.Item("ErrorObject") = expression2
                Throw exception2
            End If
            Dim parameters As Object() = Nothing
            Dim resultArray As RuleExpressionResult() = Nothing
            If ((Not expression2.Parameters Is Nothing) AndAlso (expression2.Parameters.Count > 0)) Then
                Dim count As Integer = expression2.Parameters.Count
                Dim infoArray As ParameterInfo() = methodInfo.GetParameters
                parameters = New Object(infoArray.Length  - 1) {}
                Dim length As Integer = infoArray.Length
                If info.NeedsParamsExpansion Then
                    length -= 1
                End If
                Dim index As Integer = 0
                Do While (index < length)
                    Dim expressionType As Type = execution.Validation.ExpressionInfo(expression2.Parameters.Item(index)).ExpressionType
                    Dim result As RuleExpressionResult = RuleExpressionWalker.Evaluate(execution, expression2.Parameters.Item(index))
                    Dim expression3 As CodeDirectionExpression = [TryCast](expression2.Parameters.Item(index),CodeDirectionExpression)
                    If ((Not expression3 Is Nothing) AndAlso ((expression3.Direction = FieldDirection.Ref) OrElse (expression3.Direction = FieldDirection.Out))) Then
                        If (resultArray Is Nothing) Then
                            resultArray = New RuleExpressionResult(expression2.Parameters.Count  - 1) {}
                        End If
                        resultArray(index) = result
                        If (expression3.Direction <> FieldDirection.Out) Then
                            parameters(index) = Executor.AdjustType(expressionType, result.Value, infoArray(index).ParameterType)
                        End If
                    Else
                        parameters(index) = Executor.AdjustType(expressionType, result.Value, infoArray(index).ParameterType)
                    End If
                    index += 1
                Loop
                If (length < count) Then
                    Dim info3 As ParameterInfo = infoArray(length)
                    Dim parameterType As Type = info3.ParameterType
                    Dim elementType As Type = parameterType.GetElementType
                    Dim array As Array = DirectCast(parameterType.InvokeMember(parameterType.Name, BindingFlags.CreateInstance, Nothing, Nothing, New Object() { (count - index) }, CultureInfo.CurrentCulture), Array)
                    Do While (index < count)
                        Dim operandType As Type = execution.Validation.ExpressionInfo(expression2.Parameters.Item(index)).ExpressionType
                        Dim result2 As RuleExpressionResult = RuleExpressionWalker.Evaluate(execution, expression2.Parameters.Item(index))
                        array.SetValue(Executor.AdjustType(operandType, result2.Value, elementType), CInt((index - length)))
                        index += 1
                    Loop
                    parameters(length) = array
                End If
            End If
            Try 
                obj3 = methodInfo.Invoke(obj2, parameters)
            Catch exception3 As TargetInvocationException
                If (exception3.InnerException Is Nothing) Then
                    Throw
                End If
                Throw New TargetInvocationException(String.Format(CultureInfo.CurrentCulture, Messages.Error_MethodInvoke, New Object() { RuleDecompiler.DecompileType(methodInfo.ReflectedType), methodInfo.Name, exception3.InnerException.Message }), exception3.InnerException)
            End Try
            If (Not resultArray Is Nothing) Then
                Dim i As Integer
                For i = 0 To expression2.Parameters.Count - 1
                    If (Not resultArray(i) Is Nothing) Then
                        resultArray(i).Value = parameters(i)
                    End If
                Next i
            End If
            Return New RuleLiteralResult(obj3)
        End Function

        Friend Overrides Function Match(ByVal expression As CodeExpression, ByVal comperand As CodeExpression) As Boolean
            Dim expression2 As CodeMethodInvokeExpression = DirectCast(expression, CodeMethodInvokeExpression)
            Dim expression3 As CodeMethodInvokeExpression = DirectCast(comperand, CodeMethodInvokeExpression)
            If (expression2.Method.MethodName <> expression3.Method.MethodName) Then
                Return False
            End If
            If Not RuleExpressionWalker.Match(expression2.Method.TargetObject, expression3.Method.TargetObject) Then
                Return False
            End If
            If (expression2.Parameters.Count <> expression3.Parameters.Count) Then
                Return False
            End If
            Dim i As Integer
            For i = 0 To expression2.Parameters.Count - 1
                If Not RuleExpressionWalker.Match(expression2.Parameters.Item(i), expression3.Parameters.Item(i)) Then
                    Return False
                End If
            Next i
            Return True
        End Function

        <SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")> _
        Friend Overrides Function Validate(ByVal expression As CodeExpression, ByVal validation As RuleValidation, ByVal isWritten As Boolean) As RuleExpressionInfo
            Dim expressionType As Type = Nothing
            Dim info As RuleMethodInvokeExpressionInfo = Nothing
            Dim item As ValidationError = Nothing
            Dim public As BindingFlags = BindingFlags.Public
            Dim newParent As CodeMethodInvokeExpression = DirectCast(expression, CodeMethodInvokeExpression)
            If isWritten Then
                item = New ValidationError(String.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, New Object() { GetType(CodeMethodInvokeExpression).ToString }), &H17A)
                item.UserData.Item("ErrorObject") = newParent
                validation.Errors.Add(item)
                Return Nothing
            End If
            If ((newParent.Method Is Nothing) OrElse (newParent.Method.TargetObject Is Nothing)) Then
                item = New ValidationError(String.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, New Object() { newParent.Method.MethodName }), &H53D)
                item.UserData.Item("ErrorObject") = newParent
                validation.Errors.Add(item)
                Return Nothing
            End If
            If ((Not newParent.Method.TypeArguments Is Nothing) AndAlso (newParent.Method.TypeArguments.Count > 0)) Then
                item = New ValidationError(Messages.GenericMethodsNotSupported, &H548)
                item.UserData.Item("ErrorObject") = newParent
                validation.Errors.Add(item)
                Return Nothing
            End If
            Try 
                If Not validation.PushParentExpression(newParent) Then
                    Return Nothing
                End If
                Dim info2 As RuleExpressionInfo = RuleExpressionWalker.Validate(validation, newParent.Method.TargetObject, False)
                If (info2 Is Nothing) Then
                    Return Nothing
                End If
                expressionType = info2.ExpressionType
                If (expressionType Is Nothing) Then
                    Return Nothing
                End If
                If (expressionType Is GetType(NullLiteral)) Then
                    item = New ValidationError(String.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, New Object() { newParent.Method.MethodName }), &H546)
                    item.UserData.Item("ErrorObject") = newParent
                    validation.Errors.Add(item)
                    expressionType = Nothing
                End If
                Dim argumentExprs As New List([Of] CodeExpression)
                Dim flag As Boolean = False
                If (Not newParent.Parameters Is Nothing) Then
                    Dim i As Integer
                    For i = 0 To newParent.Parameters.Count - 1
                        Dim expression3 As CodeExpression = newParent.Parameters.Item(i)
                        If (expression3 Is Nothing) Then
                            item = New ValidationError(String.Format(CultureInfo.CurrentCulture, Messages.NullMethodParameter, New Object() { i.ToString(CultureInfo.CurrentCulture), newParent.Method.MethodName }), &H53D)
                            item.UserData.Item("ErrorObject") = newParent
                            validation.Errors.Add(item)
                            expressionType = Nothing
                        Else
                            If TypeOf expression3 Is CodeTypeReferenceExpression Then
                                item = New ValidationError(String.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, New Object() { expression3.GetType.FullName }), &H548)
                                item.UserData.Item("ErrorObject") = expression3
                                validation.AddError(item)
                                flag = True
                            End If
                            If (RuleExpressionWalker.Validate(validation, expression3, False) Is Nothing) Then
                                flag = True
                            End If
                            argumentExprs.Add(expression3)
                        End If
                    Next i
                End If
                If (expressionType Is Nothing) Then
                    Return Nothing
                End If
                If flag Then
                    Return Nothing
                End If
                If TypeOf newParent.Method.TargetObject Is CodeTypeReferenceExpression Then
                    [public] = ([public] Or (BindingFlags.FlattenHierarchy Or BindingFlags.Static))
                Else
                    [public] = ([public] Or BindingFlags.Instance)
                End If
                If validation.AllowInternalMembers(expressionType) Then
                    [public] = ([public] Or BindingFlags.NonPublic)
                End If
                info = validation.ResolveMethod(expressionType, newParent.Method.MethodName, [public], argumentExprs, item)
                If ((info Is Nothing) AndAlso newParent.UserData.Contains("QualifiedName")) Then
                    Dim qualifiedName As String = [TryCast](newParent.UserData.Item("QualifiedName"),String)
                    Dim type2 As Type = validation.ResolveType(qualifiedName)
                    If (Not type2 Is Nothing) Then
                        validation.DetermineExtensionMethods(type2.Assembly)
                        info = validation.ResolveMethod(expressionType, newParent.Method.MethodName, [public], argumentExprs, item)
                    End If
                End If
                If (info Is Nothing) Then
                    item.UserData.Item("ErrorObject") = newParent
                    validation.Errors.Add(item)
                    Return Nothing
                End If
            Finally
                validation.PopParentExpression
            End Try
            Dim methodInfo As MethodInfo = info.MethodInfo
            If (methodInfo.ReturnType Is Nothing) Then
                item = New ValidationError(String.Format(CultureInfo.CurrentCulture, Messages.CouldNotDetermineMemberType, New Object() { newParent.Method.MethodName }), &H194)
                item.UserData.Item("ErrorObject") = newParent
                validation.Errors.Add(item)
                Return Nothing
            End If
            If Not validation.ValidateMemberAccess(newParent.Method.TargetObject, expressionType, methodInfo, newParent.Method.MethodName, newParent) Then
                Return Nothing
            End If
            Dim customAttributes As Object() = methodInfo.GetCustomAttributes(GetType(RuleAttribute), True)
            If ((Not customAttributes Is Nothing) AndAlso (customAttributes.Length > 0)) Then
                Dim stack As New Stack([Of] MemberInfo)
                stack.Push(methodInfo)
                Dim flag2 As Boolean = True
                Dim attribute As RuleAttribute
                For Each attribute In customAttributes
                    If Not attribute.Validate(validation, methodInfo, expressionType, methodInfo.GetParameters) Then
                        flag2 = False
                    End If
                Next
                stack.Pop
                If Not flag2 Then
                    Return Nothing
                End If
            End If
            If TypeOf methodInfo Is ExtensionMethodInfo Then
                newParent.UserData.Item("QualifiedName") = methodInfo.DeclaringType.AssemblyQualifiedName
            End If
            Return info
        End Function

    End Class
End Namespace

